/*
 * Copyright (c) 2008-2011, Petr Panteleyev
 * All rights reserved.
 *
 * Redistribution and use in source and binary forms, with or without modification,
 * are permitted provided that the following conditions are met:
 *
 *    1. Redistributions of source code must retain the above copyright notice,
 *       this list of conditions and the following disclaimer.
 *    2. Redistributions in binary form must reproduce the above copyright notice,
 *       this list of conditions and the following disclaimer in the documentation
 *       and/or other materials provided with the distribution.
 *    3. The name of the author may not be used to endorse or promote products
 *       derived from this software without specific prior written permission.
 *
 * THIS SOFTWARE IS PROVIDED BY THE AUTHOR ``AS IS'' AND ANY EXPRESS OR IMPLIED
 * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY
 * AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR
 * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
 * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 * LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
 * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING
 * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
 * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */

package org.panteleyev.wordpress;

import java.awt.event.ActionEvent;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.io.ObjectInputStream;
import java.io.ObjectOutputStream;
import java.io.OptionalDataException;
import java.io.Serializable;
import java.net.MalformedURLException;
import java.net.URL;
import java.util.ArrayList;
import java.util.HashMap;

import javax.swing.AbstractAction;
import javax.swing.Action;
import javax.swing.ImageIcon;
import javax.swing.JFrame;

import org.panteleyev.blogapi.Blog;
import org.panteleyev.blogapi.BlogEntry;
import org.panteleyev.blogapi.IBlogEntry;
import org.panteleyev.blogapi.MetaWeblogEntry;
import org.panteleyev.blogapi.WordPressAccount;
import org.panteleyev.blogapi.WordPressTag;
import org.panteleyev.blogclient.BloggerFrame;
import org.panteleyev.blogclient.IXmlRpcBased;
import org.panteleyev.blogclient.Icon;
import org.panteleyev.blogclient.MainClass;
import org.panteleyev.blogclient.ProfileDialog;
import org.panteleyev.blogclient.UserProfile;
import org.panteleyev.blogclient.UserProfileType;
import org.panteleyev.swing.WindowManager;
import org.panteleyev.swing.WindowManagerEnumerator;

public class WordPressUserProfile extends UserProfile<MetaWeblogEntry> implements IXmlRpcBased, Serializable {
    private final static long serialVersionUID = 20100206L;

    private static final String BLOGS_FILE = "blogs.pb";
    private static final String TAGS_FILE = "tags.pb";
    public  static final String XMLRPC_DEFAULT = "http://wordpress.com/xmlrpc.php";

    /* Serializable fields */
    private String  xmlRpc = XMLRPC_DEFAULT;
    private int     timeCorrection = 0;                         // obsolete field, not used

    /* Transient fields */
    private transient WordPressAccount account;

    /* Actions */
    private transient AbstractAction historyWindowAction;
    private transient AbstractAction outboxAction;
    private transient AbstractAction pagesAction;
    private transient AbstractAction commentsAction;
    private transient AbstractAction authorsAction;
    private transient ArrayList<AbstractAction> profileActions;
    private transient ArrayList<AbstractAction> toolBarActions;

    public WordPressUserProfile(UserProfileType type, String name, String server) {
        super(type, name, server);
        createActions();
    }

    private void readObject(java.io.ObjectInputStream stream) throws IOException, ClassNotFoundException {
        stream.defaultReadObject();
        createActions();
    }

    @Override
    public String getXmlRpc() {
        return xmlRpc.toString();
    }

    public WordPressAccount getAccount() {
        return account;
    }

    public void setAccount(WordPressAccount account) {
        this.account = account;
    }

    @Override
    public void setXmlRpc(String xmlRpc) {
        this.xmlRpc = xmlRpc;

        try {
            URL url = new URL(xmlRpc);
            String host = url.getHost();
            setServer(host);
        } catch (MalformedURLException ex) {
        }

    }

    @Override
    public ProfileDialog createProfileDialog(BloggerFrame frame) {
        return new WPProfileDialog(frame, this);
    }

    @Override
    public ArrayList<AbstractAction> getProfileActions() {
        return profileActions;
    }

    @Override
    public String getProfileMenuName() {
        return L10N.WORDPRESS_MENU_NAME.s();
    }

    @Override
    public ArrayList<AbstractAction> getProfileToolBarActions() {
        return toolBarActions;
    }

    private void createActions() {
        historyWindowAction = new HistoryFrameAction();
        outboxAction = new OutboxFrameAction();

        pagesAction = new AbstractAction(L10N.PAGES_ACTION.s(), Icon.PAGES.i()) {
            @Override
            public void actionPerformed(ActionEvent e) {
                onPagesAction();
            }
        };
        pagesAction.putValue(Action.SHORT_DESCRIPTION, L10N.PAGES_ACTION.s());
        pagesAction.putValue(Action.LARGE_ICON_KEY, Icon.PAGES.I());

        commentsAction = new AbstractAction(L10N.COMMENTS_ACTION.s(), Icon.COMMENTS.i()) {
            @Override
            public void actionPerformed(ActionEvent e) {
                onCommentsAction();
            }
        };
        commentsAction.putValue(Action.SHORT_DESCRIPTION, L10N.COMMENTS_ACTION.s());
        commentsAction.putValue(Action.LARGE_ICON_KEY, Icon.COMMENTS.I());

        authorsAction = new AbstractAction(L10N.AUTHORS_ACTION.s(), Icon.FRIENDS_WINDOW_ICON.i()) {
            @Override
            public void actionPerformed(ActionEvent e) {
                onAuthorsAction();
            }
        };
        authorsAction.putValue(Action.SHORT_DESCRIPTION, L10N.AUTHORS_ACTION.s());
        authorsAction.putValue(Action.LARGE_ICON_KEY, Icon.FRIENDS_WINDOW_ICON.I());

        profileActions = new ArrayList<>();
        profileActions.add(getNewPostAction());
        profileActions.add(null);
        profileActions.add(historyWindowAction);
        profileActions.add(pagesAction);
        profileActions.add(commentsAction);
        profileActions.add(authorsAction);
        profileActions.add(outboxAction);

        toolBarActions = new ArrayList<>();
        toolBarActions.add(historyWindowAction);
        toolBarActions.add(pagesAction);
        toolBarActions.add(commentsAction);
        toolBarActions.add(authorsAction);
        toolBarActions.add(outboxAction);
    }

    @Override
    public void onHistoryFrameAction() {
        JFrame frame = findFrame(WPHistoryFrame.class);
        if (frame != null) {
            frame.setVisible(true);
        } else {
            new WPHistoryFrame(this).setVisible(true);
        }
    }

    private void onPagesAction() {
        JFrame frame = findFrame(WPPagesFrame.class);
        if (frame != null) {
            frame.setVisible(true);
        } else {
            new WPPagesFrame(this).setVisible(true);
        }
    }

    private void onCommentsAction() {
        JFrame frame = findFrame(WPCommentsModerationFrame.class);
        if (frame != null) {
            frame.setVisible(true);
        } else {
            new WPCommentsModerationFrame(this).setVisible(true);
        }
    }

    private void onAuthorsAction() {
        JFrame frame = findFrame(WordPressAuthorsFrame.class);
        if (frame != null) {
            frame.setVisible(true);
        } else {
            new WordPressAuthorsFrame(this).setVisible(true);
        }
    }

    @Override
    public void postLoginUIActions() {
        boolean b = !getAccount().getBlogs().isEmpty() && isOnline();
        historyWindowAction.setEnabled(b);
        pagesAction.setEnabled(b);
        commentsAction.setEnabled(b);
        authorsAction.setEnabled(b);
    }

    @Override
    public boolean login(boolean store, final boolean offl) {
        if (this instanceof IXmlRpcBased) {
            account = new WordPressAccount(getXmlRpc());
        } else {
            throw new IllegalStateException("Wrong profile type");
        }
        setOnline(!offl);
        final String username = getUserName();
        final String password = getPasswdTemp();

        try {
            if (Thread.interrupted()) {
                return false;
            }

            if (!offl) {
                account.login(username, password);
                storeBlogs(account);
            } else {
                restoreBlogs(account);
            }
            if (Thread.interrupted()) {
                return false;
            }
            restoreTags(account);

            setAccount(account);
            setConnected(true);
            return true;
        } catch (InterruptedException e) {
            return false;
        } catch (Exception e) {
            MainClass.handleException(null, e);
            return false;
        }
    }

    @Override
    public void onNewPost() {
        new WPEditorFrame(this, null).setVisible(true);
    }

    @Override
    public WPEditorFrame findBlogEntryEditor(final MetaWeblogEntry entry) {
        return WindowManager.find(WPEditorFrame.class, new WindowManagerEnumerator<WPEditorFrame>() {
            @Override
            public boolean frameFound(WPEditorFrame frame) {
                return entry.equals(frame.getPost());
            }
        });
    }

    public WPCommentsFrame findCommentsWindowForPost(final BlogEntry event) {
        if (event == null) {
            return null;
        }

        return WindowManager.find(WPCommentsFrame.class, new WindowManagerEnumerator<WPCommentsFrame>() {
            @Override
            public boolean frameFound(WPCommentsFrame frame) {
                return frame.getItemID().equals(event.getId());
            }
        });
    }

    public void openCommentsWindow(BlogEntry event) {
        WPCommentsFrame wnd = findCommentsWindowForPost(event);

        try {
            if (wnd == null) {
                wnd = new WPCommentsFrame(this, event);
            }

            wnd.setVisible(true);
        } catch (Exception e) {
            MainClass.handleException(null, e);
        }
    }

    @Override
    public IBlogEntry newBlogEntry() {
        return new MetaWeblogEntry();
    }

    @Override
    public void openBlogEntryEditor(MetaWeblogEntry entry, File file) {
        new WPOpenPostThread(this, entry, file).execute();
    }

    @Override
    public void publishEntry(MetaWeblogEntry entry) throws Exception {
        account.createEntry(entry);
    }

    @Override
    public void onOutboxFrameAction() {
        JFrame frame = findFrame(WPOutboxFrame.class);
        if (frame != null) {
            frame.setVisible(true);
        } else {
            new WPOutboxFrame(this).setVisible(true);
        }
    }

    @Override
    public void updateEntryAdterRestore(MetaWeblogEntry entry) {
        // find corresponding blog
        Blog blog = null;
        if (entry.getBlogId() != null) {
            blog = account.getBlog(entry.getBlogId());
        }
        if (blog == null) {
            blog = account.getBlog(0);
        }
        entry.setBlog(blog);
    }

    public void storeBlogs(WordPressAccount acc) throws IOException {
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(getBlogsFile()))) {
            out.writeInt(acc.getBlogs().size());
            for (Blog blog : acc.getBlogs()) {
                out.writeObject(blog);
            }
        }
    }

    public void restoreBlogs(WordPressAccount acc) throws IOException, ClassNotFoundException {
        File fin = getBlogsFile();
        ArrayList<Blog> blogs = new ArrayList<>();

        if (fin.exists()) {
            try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(fin))) {
                int count = in.readInt();
                blogs.ensureCapacity(count);
                for (int i = 0; i < count; ++i) {
                    Blog blog = (Blog)in.readObject();
                    if (blog != null) {
                        blogs.add(blog);
                    }
                }
            }
        }

        acc.setBlogs(blogs);
    }

    public void storeTags(WordPressAccount acc) throws IOException {
        try (ObjectOutputStream out = new ObjectOutputStream(new FileOutputStream(getTagsFile()))) {
            out.writeObject(acc.getTags());
        }
    }

    public void restoreTags(WordPressAccount acc) throws IOException, ClassNotFoundException {
        File fin = getTagsFile();
        HashMap<String,ArrayList<WordPressTag>> tags = new HashMap<>();

        if (fin.exists()) {
            try (ObjectInputStream in = new ObjectInputStream(new FileInputStream(fin))) {
                try {
                    Object obj = in.readObject();
                    if (obj instanceof HashMap) {
                        tags = (HashMap<String,ArrayList<WordPressTag>>)obj;
                    }
                } catch (OptionalDataException ex) {
                }
            }
        }

        acc.setTags(tags);
    }

    public File getBlogsFile() {
        return new File(getCacheDirectory(), BLOGS_FILE);
    }

    public File getTagsFile() {
        return new File(getCacheDirectory(), TAGS_FILE);
    }

    @Override
    public ImageIcon getIcon() {
        return Icon.WORDPRESS.i();
    }

}
